#include "locations/location.h"
#include <boost/format.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/range.hpp> 

using boost::format;

namespace ba = boost::algorithm;
namespace p  = std::placeholders;

Location::Location( const std::string& name , Connection* conn )
	: name(name) , connection(conn)
{
	init_connection();
}

void Location::swap_connection( Location& loc )
{
	loc.sig_conn.disconnect();
	sig_conn.disconnect();

	std::swap( connection , loc.connection );

	loc.init_connection();
	init_connection();
}

void Location::init_connection()
{
	if( !connection )
		return;

	sig_conn = connection->on_line_received(
		std::bind(&Location::parse_line,this,std::placeholders::_1) );
}

void Location::write_line( const std::string& line )
{
	if( connection )
		connection->write_line( line );
}

void Location::send_fail( ErrorCodes code , const std::string& msg )
{
	send_fail( 0 , code , msg );
}

void Location::send_fail( id_t id , ErrorCodes code , const std::string& msg )
{
	if( msg.empty() )
		write_line( str( format("e %u %u") % (unsigned)id % (unsigned)code ) );
	else
		write_line( str( format("e %u %u \"%s\"") % (unsigned)id % (unsigned)code % msg ) );
}

bool Location::parse_line( const std::string& line )
{
	auto gap1 = ba::find_token( line, ba::is_any_of(" \t"), ba::token_compress_on );

	// initialize all parsed fields in case we return prematurely to caller
	cmd_name = std::string( line.begin() , gap1.begin() );
	cmd_id = 0;
	cmd_data = std::string();

	auto remaining_line = boost::make_iterator_range( gap1.end() , line.end() );
	auto gap2 = ba::find_token( remaining_line , ba::is_any_of(" \t"), ba::token_compress_on );

	std::string cmd_id_str( gap1.end() , gap2.begin() );

	if( cmd_name.empty() || cmd_id_str.empty() ) {
		send_fail( ErrorCodes::ill_formed );
		return true;
	}

	try {
		cmd_id = boost::lexical_cast<id_t>( cmd_id_str );
	} catch( const boost::bad_lexical_cast &) {
		send_fail( ErrorCodes::invalid_id );
		return true;
	}

	cmd_data = std::string( gap2.end() , line.end() );

	return false;
}

id_t Location::get_cmd_id() const
{
	return cmd_id;
}

std::string Location::get_cmd_name() const
{
	return cmd_name;
}

std::string Location::get_cmd_data() const
{
	return cmd_data;
}
